#include "nodecamera.h"
#include "../../core/resources/resourcemanager.h"
#include "../../core/resources/resourceidentifiers.h"

namespace gunsmith
{
CNodeCamera::CNodeCamera(float fFieldOfView, float fAspect, float fNear, float fFar, IDirect3DDevice9 *pDevice) : CNodeWorldObject(NT_CAMERA)
{
	m_bAttachedToObject	= false;
	m_pObjectAttachedTo	= NULL;
	m_bLooksAtObject	= false;
	m_pObjectLookedAt	= NULL;
	m_fAspect			= fAspect;
	m_fFar				= fFar;
	m_fFieldOfView		= fFieldOfView;
	m_fNear				= fNear;
	m_vUp				= D3DXVECTOR3(0, 1, 0);
	m_vPosition			= D3DXVECTOR3(0, 0, 0);
	m_vLookAt			= D3DXVECTOR3(0, 0, 0);

	m_xProjectionMatrix.pData	= &m_matProjection;
	m_xProjectionMatrix.eType	= VT_MATRIX;
	m_xViewMatrix.pData			= &m_matView;
	m_xViewMatrix.eType			= VT_MATRIX;

	m_ResEVars = m_pResMngr->GetResourcesByName<CResourceEffectVariables>(RID_EFFECT_VARIABLES);

	UpdateAttributes();
}

CNodeCamera::~CNodeCamera()
{

}

/*!	\brief		Updates camera attributes like view and projection matrix.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::UpdateAttributes()
{
	// if camera looks at/is attached to an object take first, otherwise second
	const D3DXVECTOR3& vPosition = m_bAttachedToObject	? m_pObjectAttachedTo->GetPosition() : m_vPosition;
	const D3DXVECTOR3& vLookAt	 = m_bLooksAtObject		? m_pObjectLookedAt->GetPosition()	 : m_vLookAt;

	D3DXMatrixLookAtLH(&m_matView, &vPosition, &vLookAt, &m_vUp);
	D3DXMatrixPerspectiveFovLH(&m_matProjection, m_fFieldOfView, m_fAspect, m_fNear, m_fFar);
}

/*!	\brief		Sets the field of view in radiant.
	\param		fFieldOfView	Field of view (rad).
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::SetFieldOfView(float fFieldOfView)
{
	m_fFieldOfView = fFieldOfView;
	UpdateAttributes();
}

/*!	\brief		Sets near and far plane for frustrum culling.
	\param		fNear	Near plane.
	\param		fFar	Far plane.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::SetPlanes(float fNear, float fFar)
{
	m_fNear	= fNear;
	m_fFar	= fFar;
	UpdateAttributes();
}

/*!	\brief		Sets aspect ration of the view.
	\param		fAspect	Aspect ratio.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::SetAspect(float fAspect)
{
	m_fAspect = fAspect;
	UpdateAttributes();
}

/*!	\brief		Let's the camera look at a given position.
	\param		vLookAt	Position to look at.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::LookAt(const D3DXVECTOR3& vLookAt)
{
	m_vLookAt = vLookAt;
	UpdateAttributes();
}

/*!	\brief		Sets the up vector for the camera.
	\param		vUp		Up vector.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::SetUp(const D3DXVECTOR3& vUp)
{
	m_vUp = vUp;
	UpdateAttributes();
}

/*!	\brief		Attaches camera to a world object and follow its motion directly.
	\param		pWorldObject	World object the camra is attached to.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::AttachTo(const CNodeWorldObject *pWorldObject)
{
	m_bAttachedToObject = true;
	m_pObjectAttachedTo = pWorldObject;
}

/*!	\brief		Detaches the camera from an object and will use last set position as the new eye position.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::Detach()
{
	m_bAttachedToObject = false;
	m_pObjectAttachedTo = NULL;
}

/*!	\brief		Camera will focus on the given object and will look at GetPosition() of the object. (see remarks)
	\param		pWorldObject Object to focus the camera on.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::FocusOn(const CNodeWorldObject *pWorldObject)
{
	m_bLooksAtObject = true;
	m_pObjectLookedAt = pWorldObject;
}

/*!	\brief		Won't follow the focus object anymore and will look at the position last set. (see remarks)
	\return		void
	\remarks	Last position set by LookAt(const D3DXVECTOR3& vLookAt). If no position has been set camera will look at (0, 0, 0).
	\sa			*/
void CNodeCamera::DropFocus()
{
	m_bLooksAtObject = false;
	m_pObjectLookedAt = NULL;
}

/*!	\brief		Not used as of yet.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::Draw()
{
}

/*!	\brief		Update is used to update camera transformation and to set view and projection matrices.
	\param		fDelta	Delta time.
	\param		pData	Additional data.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::Update(float fDelta, void *pData)
{
	__super::Update(fDelta);	// need to invoke __super::Update() to update position changes via direction
	//effect variables here
	m_ResEVars->UpdateVariable("CameraViewMatrix", &m_xViewMatrix);
	m_ResEVars->UpdateVariable("CameraProjectionMatrix", &m_xProjectionMatrix);
}

/*!	\brief		Sets camera position.
	\param		vPosition	Camera position.
	\return		void
	\remarks	
	\sa			*/
void CNodeCamera::SetPosition(const D3DXVECTOR3& vPosition)
{
	__super::SetPosition(vPosition);
	UpdateAttributes();
}
};